/**
 * Logger interface
 */
export interface ILogger {
    /**
     * print debug message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    debug(domain: string, tag: string, message: string) : void;

    /**
     * print info message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    info(domain: string, tag: string, message: string) : void;

    /**
     * print warn message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    warn(domain: string, tag: string, message: string) : void;

    /**
     * print error message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    error(domain: string, tag: string, message: string) : void;

    /**
     * print fatal message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    fatal(domain: string, tag: string, message: string) : void;
}

/**
 * Log Type
 */
export enum LogType {
    DEBUG = 'DEBUG',
    INFO = 'INFO',
    WARN = 'WARN',
    ERROR = 'ERROR',
    FATAL = 'FATAL'
}

/**
 * Loggable interface
 */
export interface ILoggable {
    /**
     * Domain
     */
    DOMAIN: string;
    /**
     * Tag
     */
    TAG: string;
}

/**
 * Logger class
 */
class Logger implements ILogger {
    private _enableTime: boolean;

    constructor(enableTime: boolean = true) {
        this._enableTime = enableTime;
    }

    /**
     * print log
     * @param logType log type
     * @param domain domain
     * @param tag tag
     * @param message message
     */
    private print(logType: LogType, domain: string, tag: string, message: string) {
        const curTime = this._enableTime ? `${new Date().toISOString().replace('Z', '')}` : '*';
        console.log(`${curTime} | [${logType}] | ${domain}-${tag}: ${message}`);
    }

    /**
     * print debug message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public debug(domain: string, tag: string, message: string) {
        this.print(LogType.DEBUG, domain, tag, message)
    }

    /**
     * print info message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public info(domain: string, tag: string, message: string) {
        this.print(LogType.INFO, domain, tag, message)
    }

    /**
     * print warn message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public warn(domain: string, tag: string, message: string) {
        this.print(LogType.WARN, domain, tag, message)
    }

    /**
     * print error message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public error(domain: string, tag: string, message: string) {
        this.print(LogType.ERROR, domain, tag, message)
    }

    /**
     * print fatal message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public fatal(domain: string, tag: string, message: string) {
        this.print(LogType.FATAL, domain, tag, message)
    }
}

/**
 * Log class
 */
export class Log {
    private static _logger: ILogger = new Logger(true);

    /**
     * set logger instance
     * @param logger logger instance
     */
    public static setLogger(logger: ILogger) {
        this._logger = logger;
    }

    /**
     * print debug message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public static debug(domain: string, tag: string, message: string) {
        this._logger.debug(domain, tag, message);
    }

    /**
     * print info message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public static info(domain: string, tag: string, message: string) {
        this._logger.info(domain, tag, message);
    }

    /**
     * print warn message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public static warn(domain: string, tag: string, message: string) {
        this._logger.warn(domain, tag, message);
    }

    /**
     * print error message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public static error(domain: string, tag: string, message: string) {
        this._logger.error(domain, tag, message);
    }

    /**
     * print fatal message
     * @param domain log domain
     * @param tag log tag
     * @param message log message
     */
    public static fatal(domain: string, tag: string, message: string) {
        this._logger.fatal(domain, tag, message);
    }

    /**
     * print debug message
     * @param instance loggable instance
     * @param message log message
     */
    public static debugX(instance: ILoggable, message: string) {
        this._logger.debug(instance.DOMAIN, instance.TAG, message);
    }

    /**
     * print info message
     * @param instance loggable instance
     * @param message log message
     */
    public static infoX(instance: ILoggable, message: string) {
        this._logger.info(instance.DOMAIN, instance.TAG, message);
    }

    /**
     * print warn message
     * @param instance loggable instance
     * @param message log message
     */
    public static warnX(instance: ILoggable, message: string) {
        this._logger.warn(instance.DOMAIN, instance.TAG, message);
    }

    /**
     * print error message
     * @param instance loggable instance
     * @param message log message
     */
    public static errorX(instance: ILoggable, message: string) {
        this._logger.error(instance.DOMAIN, instance.TAG, message);
    }

    /**
     * print fatal message
     * @param instance loggable instance
     * @param message log message
     */
    public static fatalX(instance: ILoggable, message: string) {
        this._logger.fatal(instance.DOMAIN, instance.TAG, message);
    }
}